<template>
  <div id="app">
    <div id="rightbar">
      <SideBar ref="cardComponent" @childClick="filterGraphBylabel"
               style="height: 650px;box-shadow: none;background: linear-gradient(360deg, rgb(255, 255, 255), rgb(8, 42, 96));"></SideBar>
    </div>
    <div id="graph"></div>
  </div>
</template>

<script>
import * as d3 from "d3";
import {
  getNodeSelfPath,
  setLinkGroup,
  getNodesLine,
} from '@/services/deviceRelation'
import {getInitData, getDataBylabels} from '@/services/axios'
import SideBar from "@/components/SideBar.vue";

export default {
  components: {
    SideBar,
  },
  data() {
    return {
      checkList: ['User', 'Chapter', 'Ability', 'Problem', 'Class']
    };
  },
  async mounted() {

    let mydata = await getInitData()
    this.renderGraph(mydata)

  },
  methods: {
    updataCardData(node) {
      window.console.log(node)
      window.console.log('子函数', this.$refs.cardComponent)
      let data = {
        name: node.name,
        description: node.description,
        url: node.url,
      }
      if (this.$refs.cardComponent) {
        this.$refs.cardComponent.updateData(data);
      } else {
        window.console.error("子组件引用未定义");
      }
    },
    async renderNewGraph() {
      let mydata = await getInitData()
      this.renderGraph(mydata)
    },
    async filterGraphBylabel() {
      let mydata = await getDataBylabels(this.checkList); // 通过当前的 checkList 获取过滤后的数据
      this.renderGraph(mydata);
    },
    onCheckboxChange(label) {
      const {restoreHiddenNodesAndLinksByLabel, hideNodeAndLinksBylabel} = this.renderGraph()

      // 更新 checkList 数据
      const index = this.checkList.indexOf(label);
      if (index > -1) {
        this.checkList.splice(index, 1); // 如果已选中，则移除
      } else {
        this.checkList.push(label); // 如果没有选中，则添加
      }

      // 根据新的 checkList 数据重新筛选图表
      if (this.checkList.includes(label)) {
        restoreHiddenNodesAndLinksByLabel(label);
      } else {
        hideNodeAndLinksBylabel(label);
      }
    },
    renderGraph(data) {
      let {links, nodes} = data


      //关系分组
      setLinkGroup(links);
      d3.select("#graph").html('');

      const force = d3.layout.force()//layout将json格式转化为力学图可用的格式
          .nodes(nodes)
          .links(links)
          .linkDistance(150)
          .charge(-400)
          .gravity(0.01)
          .size([800, 800]) // 设置布局尺寸
          .on("tick", tick)
          .start();
      setTimeout(() => {
        force
            .gravity(0.05) // 增加向心力
            .start();
      }, 1000);

      let zoom = d3.behavior.zoom()
          .scaleExtent([.4, 2])
          .on("zoom", zoomed);

      const svg = d3.select("#graph").append("svg")
          .attr("pointer-event", "all")
          .attr("preserveAspectRatio", "xMidYMid meet")//自适应容器大小
          .attr("viewBox", "0 0 800 800")
          .call(zoom);

      //设置连接线
      let edges_line = svg.append("g").selectAll(".edgepath")
          .data(force.links())
          .enter()
          .append("path")
          .attr("marker-end", function (d, i) {
            return getMarkerArrow(i);
          })//根据箭头标记的id号标记箭头
          .style("stroke", '#9aaabf')
          .style("stroke-width", 1)//线条粗细
          .style("fill-opacity", 0)
          .style("cursor", "pointer")
          .attr("id", function (d, i) {
            return 'edgepath' + i;
          })
          .on("mouseover", function (d) {
            return getStrokeWidth(d);
          })
          .on("mouseout", function () {
            edges_line.style("stroke-width", 1)
          })
          .on('click', (d, i) => {
            deleteLine(d, i);
          });


      //定义连线上文字和显示
      let edges_text = svg.append("g").selectAll(".edgetext")
          .data(links)
          .enter()
          .append("text")
          .style("pointer-events", "none")
          .attr("class", "linetext")
          .attr('text-anchor', "middle")
          .attr("fill-opacity", 1)
          .style("cursor", "pointer")
          .attr({
            'class': 'edgelabel', 'id': function (d, i) {
              return 'edgepath' + i;
            }, 'dx': 50, 'dy': 0, 'fill': '#9aaabf'
          });

      //设置线条上的文字
      edges_text.append('textPath')
          .attr('xlink:href', function (d, i) {
            return '#edgepath' + i
          })
          .attr("pointer-events", "none").attr("font-size", 9)
          .text(function (d) {
            return d.relation.relationship;
          });

      let circle_g = svg.selectAll("circle")
          .data(force.nodes())//表示使用force.nodes数据
          .enter()
          .append("g")
          .attr("class", function (node, i) {
            return "g_circle_" + i;
          })//标记circle的父节点
          .style("cursor", "pointer")
          .call(drag()) //将当前选中的元素传到drag函数中，使顶点可以被拖动
          .on("click", (node, i) => {
            if (d3.event.defaultPrevented) return;//阻止click事件和拖拽事件冲突
            circleClick(node, i, this);
          })
          .on('mouseover', () => {
            if (d3.event.defaultPrevented) return;
            // showCircleBorderOuterArc(node, i);
          });

      svg.on("dblclick.zoom", null);//取消svg和圆圈的双击放大事件（d3中默认开启7个事件，关闭防止与上面的双击事件冲突）
      circle_g.on("dblclick.zoom", null);

      //圆圈
      const circle = circle_g.append("circle")
          .style("stroke-width", "2px")
          .attr("r", 30)//设置圆圈半径
          .style("fill", function (node) {
            return getCircleColor(node);
          });

      let text = circle_g.append("text")
          .attr("dy", ".35em")
          .attr("text-anchor", "middle")//在圆圈中加上数据
          .style('fill', "#FFFFFF")
          .attr('x', function (d) {
            appendCircleText(d, this);
          });

      //圆圈的提示文字
      circle.append("svg:title").text(function (node) {
        switch (node.label) {
          case 'Chapter':
            return node.name;
          case 'User':
            return node.name;
          case 'Class':
            return node.name;
          case 'Problem':
            return node.name;
          case 'Ability':
            return node.name;
          case 'Knowledge_point':
            return node.name;
        }
      });
      text.append("svg:title").text(function (node) {
        switch (node.label) {
          case 'Chapter':
            return node.name;
          case 'User':
            return node.name;
          case 'Class':
            return node.name;
          case 'Problem':
            return node.name;
          case 'Ability':
            return node.name;
          case 'Knowledge_point':
            return node.name;
          default :
            return node.name;
        }
      });

      function zoomed() {//svg下的g标签移动大小
        svg.selectAll("g").attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
      }

      function getMarkerArrow(i) {
        svg.append("defs").append("marker")   //箭头
            .attr("id", "arrow" + i)
            .attr("markerUnits", "strokeWidth")//设置为strokeWidth箭头会随着线的粗细发生变化
            .attr("markerUnits", "userSpaceOnUse")
            .attr("markerWidth", 10)//标识的大小
            .attr("markerHeight", 10)
            .attr("viewBox", "0 -4 12 15")//坐标系的区域
            .attr("refX", 0)//箭头坐标
            .attr("refY", 0)
            .attr("orient", 'auto')//绘制方向，可设定为：auto（自动确认方向）和 角度值
            .append("svg:path")
            .attr("stroke-width", 1)//箭头宽度
            .attr("d", "M0,-5L10,0L0,5")//箭头的路径
            .attr('fill', 'rgba(0,0,0, 0.4)');//箭头颜色
        return "url(#arrow" + i + ")";
      }

      function getStrokeWidth(d) {
        edges_line.style("stroke-width", function (edge) {
          return edge.relation.relationshipId === d.relation.relationshipId ? 3 : 1;
        });
      }

      function drag() {//拖拽函数
        return force.drag().on("dragstart", function () {
          d3.event.sourceEvent.stopPropagation(); //取消默认事件
          // d.fixed = true;//拖拽开始后设定被拖拽对象为固定
        });
      }

      const send = (node) => {
        this.updataCardData(node)
      }

      function circleClick(node) {
        send(node)
        svg.selectAll("circle")
            .style("fill", function (d) {
              return getCircleColor(d);
            });

        // 高亮当前点击节点
        d3.select(this).select("circle").style("fill", "orange");

        //清除上个节点的圆圈以及图标
        svg.selectAll('.g_circle_path').remove();
        svg.selectAll('.remove_a').remove();
        svg.selectAll('.hide_a').remove();
        svg.selectAll('.unlock_a').remove();
      }

      //设置圆圈的颜色
      function getCircleColor(node) {
        let color = {
          'Sections': "#FF9D00",
          'Chapter': "rgba(196,119,233,0.76)",
          'Games': '#67CAF4',
          'Categorys': '#BCDD73',
          'Knoeledge_point': '#DD1E9E',
          'Ability': '#DDBA9E',
          'IotInfrastructure': '#6ca5dd',
          'DeviceAdmin': '#50DD87',
        };//圆圈背景色
        return color[node.label] || '#C477E9';
      }

      //设置文字
      function appendCircleText(d, _this) {
        let circleText = d.name
        //如果小于四个字符，不换行
        if (circleText && circleText.length > 5) {
          circleText = circleText.substring(0, 5) + "...";
          d3.select(_this).text(function () {
            return '';
          });
        }
        d3.select(_this).append('tspan').attr('x', 0).attr('y', 0).attr("font-size", 12)
            .text(function () {
              return circleText;
            });
      }

      function deleteLine(d, i) {
        window.console.log(d, i)
      }

      function tick() {
        circle.attr("transform", transform1);//圆圈
        text.attr("transform", transform2);//顶点文字
        edges_line.attr('d', function (d) {
          //绘制自己指向自己的路径
          if (d.relation.isSelf) {
            return getNodeSelfPath(d)
          }
          //绘制两个节点之间的路径
          return getNodesLine(d);//曲线路径
        });

      }

      //设置圆圈和文字的坐标
      function transform1(d) {
        return "translate(" + d.x + "," + d.y + ")";
      }

      function transform2(d) {
        return "translate(" + (d.x) + "," + d.y + ")";
      }
    },

  }
}
</script>

<style scoped>
#app {
  display: flex;
  align-items: center;
}

#graph {
  width: 70%;
  height: 80vh;
  margin: 0 auto;
}

.rightbar {
  width: 20%;
}
</style>
